﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace TunaInterpreter2.Classes
{
    class RunScript : iBuild
    {
        ScriptData data;
        MainWindow mw;
        string code;
        string[] reservedKeywords = { "int", "float", "string", "jumpnij", "print", "println" };

        public RunScript()
        {
            mw = null;
            code = String.Empty;
            data = new ScriptData();
        }

        public bool Start(string code, MainWindow window)
        {
            Console.WriteLine("RunCode starting...");
            this.code = code;
            this.mw = window;

            this.run();

            return true;
        }

        private void log(string msg, ConsoleMsgType type)
        {
            mw.Print(type, msg);
        }

        private string trimMultipleSpace(string s)
        {
            RegexOptions options = RegexOptions.None;
            Regex regex = new Regex(@"[ ]{2,}", options);
            return regex.Replace(s, @" ");
        }

        void printDataStructure()
        {
            log("", ConsoleMsgType.INFO);
            log("-----------------------------------", ConsoleMsgType.INFO);
            log("PRINTING ALL VARIABLES", ConsoleMsgType.INFO);
            log("### INT", ConsoleMsgType.INFO);

            foreach (var row in data.intVariables)
            {
                log("KEY: " + row.Key + " VALUE: " + row.Value, ConsoleMsgType.INFO);
            }

            log("### FLOAT", ConsoleMsgType.INFO);

            foreach (var row in data.floatVariables)
            {
                log("KEY: " + row.Key + " VALUE: " + row.Value, ConsoleMsgType.INFO);
            }

            log("### STRING", ConsoleMsgType.INFO);

            foreach (var row in data.stringVariables)
            {
                log("KEY: " + row.Key + " VALUE: " + row.Value, ConsoleMsgType.INFO);
            }

            log("### LABEL", ConsoleMsgType.INFO);

            foreach (var row in data.labelsList)
            {
                log("KEY: " + row.Key + " LINE: " + (row.Value).ToString(), ConsoleMsgType.INFO);
            }

            log("-----------------------------------", ConsoleMsgType.INFO);
            log("", ConsoleMsgType.INFO);
        }

        void run()
        {
            string[] lines = code.Split(new[] { '\r', '\n' });
            this.data.source = new string[lines.Length];

            /* PIERWSZE PRZELECENIE KODU -- SZUKANIE ETYKIET */
            for (int i = 0; i < lines.Length; i++)
            {
                string originalLine = lines[i];
                string currentLine = lines[i].Trim();
                currentLine = trimMultipleSpace(currentLine);

                if (currentLine.Trim().Length > 0)
                {
                    /* --------- ETYKIETY --------- */
                    if (checkLabel(currentLine, i))
                    {
                        continue;
                    }
                }
            }

            /* DRUGIE PRZELECENIE KODU -- RESZTA WYKONANIA */
            for (int i = 0; i < lines.Length; i++)
            {
                string originalLine = lines[i];
                string currentLine = lines[i].Trim();
                currentLine = trimMultipleSpace(currentLine);

                if (currentLine.Trim().Length > 0)
                {
                    /* --------- DEFINICJE --------- */
                    if (checkDefinitions(currentLine))
                    {
                        //log("CHECK DEFINITION DONE", ConsoleMsgType.INFO);
                        continue;
                    }

                    /* --------- PRZYPISANIE WARTOSCI DO ZMIENNEJ --------- */
                    if (assignValue(currentLine, originalLine))
                    {
                        //log("ASSIGN VALUE DONE", ConsoleMsgType.INFO);
                        continue;
                    }

                    /* --------- WYPISANIE WARTOSCI NA EKRAN --------- */
                    if (printValue(currentLine))
                    {
                        //log("PRINT VALUE DONE", ConsoleMsgType.INFO);
                        continue;
                    }

                    /* --------- JUMPNIECIA --------- */
                    if(makeJump(currentLine, ref i))
                    {
                        continue;
                    }

                    /* --------- IF-ELSE --------- */
                    if (processIf(currentLine, ref i))
                    {
                        continue;
                    }

                    log("COULDN'T PROCESS LINE -- " + currentLine, ConsoleMsgType.ERROR);
                }
            }

            printDataStructure();
        }

        bool checkDefinitions(string currentLine)
        {
            //Definicja int
            if (currentLine.Contains("int") && !currentLine.Contains("print"))
            {
                data.addInt(currentLine);
                return true;
            }

            //Definicja float
            if (currentLine.Contains("float"))
            {
                data.addFloat(currentLine);
                return true;
            }

            //Definicja string
            if (currentLine.Contains("string"))
            {
                data.addString(currentLine);
                return true;
            }

            return false;
        }

        bool assignValue(string currentLine, string originalLine)
        {
            //Przypisanie wartosci do zmiennej
            if (currentLine.Contains('='))
            {
                string variable = currentLine.Substring(0, currentLine.IndexOf('=')).Trim();

                //Szukanie zmiennej w slowniku INT
                if (assignValue_Int(currentLine, variable)) return true;

                //Szukanie zmiennej w slowniku FLOAT
                if (assignValue_Float(currentLine, variable)) return true;

                //Szukanie zmiennej w slowniku STRING
                if (assignValue_String(currentLine, variable, originalLine)) return true;
            }

            return false;
        }

        bool assignValue_Int(string currentLine, string variable)
        {
            if (data.intVariables.ContainsKey(variable))
            {
                string value = currentLine.Substring(currentLine.IndexOf('=') + 1).Trim();

                try
                {
                    int v = Int32.Parse(value);
                    data.intVariables[variable] = v;
                    return true;
                }
                catch (Exception e)
                {
                }

                //Wystapil blad czyli nie ma przypisania a jakas operacja
                /* DODAWANIE */
                if (value.Contains('+'))
                {
                    string v1 = value.Substring(0, value.IndexOf('+')).Trim();
                    string v2 = value.Substring(value.IndexOf('+') + 1).Trim();

                    //Sprawdzenie czy v1 i v2 to cyfry czy zmienne
                    int int1 = 0, int2 = 0;
                    try
                    {
                        int1 = Int32.Parse(v1);
                    }
                    catch (Exception e)
                    {
                        //log("First value is variable" + " --> " + v1.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.intVariables.ContainsKey(v1))
                        {
                            int1 = data.intVariables[v1];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v1 + ", default value: " + int1, ConsoleMsgType.INFO);
                        }
                    }

                    try
                    {
                        int2 = Int32.Parse(v2);
                    }
                    catch (Exception e)
                    {
                        //log("Second value is variable" + " --> " + v2.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.intVariables.ContainsKey(v2))
                        {
                            int2 = data.intVariables[v2];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v2 + ", default value: " + int2, ConsoleMsgType.INFO);
                        }
                    }

                    data.intVariables[variable] = int1 + int2;
                    return true;
                }

                /* ODEJMOWANIE */
                if (value.Contains('-'))
                {
                    string v1 = value.Substring(0, value.IndexOf('-')).Trim();
                    string v2 = value.Substring(value.IndexOf('-') + 1).Trim();

                    //Sprawdzenie czy v1 i v2 to cyfry czy zmienne
                    int int1 = 0, int2 = 0;
                    try
                    {
                        int1 = Int32.Parse(v1);
                    }
                    catch (Exception e)
                    {
                        //log("First value is variable" + " --> " + v1.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.intVariables.ContainsKey(v1))
                        {
                            int1 = data.intVariables[v1];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v1 + ", default value: " + int1, ConsoleMsgType.INFO);
                        }
                    }

                    try
                    {
                        int2 = Int32.Parse(v2);
                    }
                    catch (Exception e)
                    {
                        //log("Second value is variable" + " --> " + v2.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.intVariables.ContainsKey(v2))
                        {
                            int2 = data.intVariables[v2];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v2 + ", default value: " + int2, ConsoleMsgType.INFO);
                        }
                    }

                    data.intVariables[variable] = int1 - int2;
                    return true;
                }

                /* MNOZENIE */
                if (value.Contains('*'))
                {
                    string v1 = value.Substring(0, value.IndexOf('*')).Trim();
                    string v2 = value.Substring(value.IndexOf('*') + 1).Trim();

                    //Sprawdzenie czy v1 i v2 to cyfry czy zmienne
                    int int1 = 0, int2 = 0;
                    try
                    {
                        int1 = Int32.Parse(v1);
                    }
                    catch (Exception e)
                    {
                        //log("First value is variable" + " --> " + v1.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.intVariables.ContainsKey(v1))
                        {
                            int1 = data.intVariables[v1];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v1 + ", default value: " + int1, ConsoleMsgType.INFO);
                        }
                    }

                    try
                    {
                        int2 = Int32.Parse(v2);
                    }
                    catch (Exception e)
                    {
                        //log("Second value is variable" + " --> " + v2.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.intVariables.ContainsKey(v2))
                        {
                            int2 = data.intVariables[v2];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v2 + ", default value: " + int2, ConsoleMsgType.INFO);
                        }
                    }

                    data.intVariables[variable] = int1 * int2;
                    return true;
                }

                /* DZIELENIE */
                if (value.Contains('/'))
                {
                    string v1 = value.Substring(0, value.IndexOf('/')).Trim();
                    string v2 = value.Substring(value.IndexOf('/') + 1).Trim();

                    //Sprawdzenie czy v1 i v2 to cyfry czy zmienne
                    int int1 = 0, int2 = 0;
                    try
                    {
                        int1 = Int32.Parse(v1);
                    }
                    catch (Exception e)
                    {
                        //log("First value is variable" + " --> " + v1.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.intVariables.ContainsKey(v1))
                        {
                            int1 = data.intVariables[v1];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v1 + ", default value: " + int1, ConsoleMsgType.INFO);
                        }
                    }

                    try
                    {
                        int2 = Int32.Parse(v2);
                    }
                    catch (Exception e)
                    {
                        //log("Second value is variable" + " --> " + v2.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.intVariables.ContainsKey(v2))
                        {
                            int2 = data.intVariables[v2];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v2 + ", default value: " + int2, ConsoleMsgType.INFO);
                        }
                    }

                    if (int2 == 0)
                    {
                        log("Couldn't divide by 0 ! Changing second value to 1", ConsoleMsgType.WARNING);
                        int2 = 1;
                    }
                    data.intVariables[variable] = int1 / int2;
                    return true;
                }
            }

            return false;
        }

        bool assignValue_Float(string currentLine, string variable)
        {
            if (data.floatVariables.ContainsKey(variable))
            {
                string value = currentLine.Substring(currentLine.IndexOf('=') + 1).Trim();

                try
                {
                    float v = (float)Double.Parse(value.Replace('.', ','));
                    data.floatVariables[variable] = v;
                    return true;
                }
                catch (Exception e)
                {

                }

                //Wystapil blad czyli nie ma przypisania a jakas operacja
                /* DODAWANIE */
                if (value.Contains('+'))
                {
                    string v1 = value.Substring(0, value.IndexOf('+')).Trim().Replace('.', ',');
                    string v2 = value.Substring(value.IndexOf('+') + 1).Trim().Replace('.', ',');

                    //Sprawdzenie czy v1 i v2 to cyfry czy zmienne
                    float float1 = 0, float2 = 0;
                    try
                    {
                        float1 = (float)Double.Parse(v1);
                    }
                    catch (Exception e)
                    {
                        //log("First value is variable" + " --> " + v1.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.floatVariables.ContainsKey(v1))
                        {
                            float1 = data.floatVariables[v1];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v1 + ", default value: " + float1, ConsoleMsgType.INFO);
                        }
                    }

                    try
                    {
                        float2 = (float)Double.Parse(v2);
                    }
                    catch (Exception e)
                    {
                        //log("Second value is variable" + " --> " + v2.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.floatVariables.ContainsKey(v2))
                        {
                            float2 = data.floatVariables[v2];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v2 + ", default value: " + float2, ConsoleMsgType.INFO);
                        }
                    }

                    data.floatVariables[variable] = float1 + float2;
                    return true;
                }

                /* ODEJMOWANIE */
                if (value.Contains('-'))
                {
                    string v1 = value.Substring(0, value.IndexOf('-')).Trim().Replace('.', ',');
                    string v2 = value.Substring(value.IndexOf('-') + 1).Trim().Replace('.', ',');

                    //Sprawdzenie czy v1 i v2 to cyfry czy zmienne
                    float float1 = 0, float2 = 0;
                    try
                    {
                        float1 = (float)Double.Parse(v1);
                    }
                    catch (Exception e)
                    {
                        //log("First value is variable" + " --> " + v1.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.floatVariables.ContainsKey(v1))
                        {
                            float1 = data.floatVariables[v1];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v1 + ", default value: " + float1, ConsoleMsgType.INFO);
                        }
                    }

                    try
                    {
                        float2 = (float)Double.Parse(v2);
                    }
                    catch (Exception e)
                    {
                        //log("Second value is variable" + " --> " + v2.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.floatVariables.ContainsKey(v2))
                        {
                            float2 = data.floatVariables[v2];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v2 + ", default value: " + float2, ConsoleMsgType.INFO);
                        }
                    }

                    data.floatVariables[variable] = float1 - float2;
                    return true;
                }

                /* MNOZENIE */
                if (value.Contains('*'))
                {
                    string v1 = value.Substring(0, value.IndexOf('*')).Trim().Replace('.', ',');
                    string v2 = value.Substring(value.IndexOf('*') + 1).Trim().Replace('.', ',');

                    //Sprawdzenie czy v1 i v2 to cyfry czy zmienne
                    float float1 = 0, float2 = 0;
                    try
                    {
                        float1 = (float)Double.Parse(v1);
                    }
                    catch (Exception e)
                    {
                        //log("First value is variable" + " --> " + v1.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.floatVariables.ContainsKey(v1))
                        {
                            float1 = data.floatVariables[v1];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v1 + ", default value: " + float1, ConsoleMsgType.INFO);
                        }
                    }

                    try
                    {
                        float2 = (float)Double.Parse(v2);
                    }
                    catch (Exception e)
                    {
                        //log("Second value is variable" + " --> " + v2.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.floatVariables.ContainsKey(v2))
                        {
                            float2 = data.floatVariables[v2];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v2 + ", default value: " + float2, ConsoleMsgType.INFO);
                        }
                    }

                    data.floatVariables[variable] = float1 * float2;
                    return true;
                }

                /* DZIELENIE */
                if (value.Contains('/'))
                {
                    string v1 = value.Substring(0, value.IndexOf('/')).Trim().Replace('.', ',');
                    string v2 = value.Substring(value.IndexOf('/') + 1).Trim().Replace('.', ',');

                    //Sprawdzenie czy v1 i v2 to cyfry czy zmienne
                    float float1 = 0, float2 = 0;
                    try
                    {
                        float1 = (float)Double.Parse(v1);
                    }
                    catch (Exception e)
                    {
                        //log("First value is variable" + " --> " + v1.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.floatVariables.ContainsKey(v1))
                        {
                            float1 = data.floatVariables[v1];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v1 + ", default value: " + float1, ConsoleMsgType.INFO);
                        }
                    }

                    try
                    {
                        float2 = (float)Double.Parse(v2);
                    }
                    catch (Exception e)
                    {
                        //log("Second value is variable" + " --> " + v2.Replace(' ', '^'), ConsoleMsgType.WARNING);
                        if (data.floatVariables.ContainsKey(v2))
                        {
                            float2 = data.floatVariables[v2];
                        }
                        else
                        {
                            log("Couldn't find variable: " + v2 + ", default value: " + float2, ConsoleMsgType.INFO);
                        }
                    }

                    if (float2 == 0)
                    {
                        log("Couldn't divide by 0 ! Changing second value to 1", ConsoleMsgType.WARNING);
                        float2 = 1;
                    }

                    data.floatVariables[variable] = float1 / float2;
                    return true;
                }
            }

            return false;
        }

        bool assignValue_String(string currentLine, string variable, string originalLine)
        {
            if (data.stringVariables.ContainsKey(variable))
            {
                string value = originalLine.Substring(originalLine.IndexOf('%') + 1, originalLine.LastIndexOf('%') - 1 - originalLine.IndexOf('%'));
                data.stringVariables[variable] = value;
                return true;
            }

            return false;
        }

        bool printValue(string currentLine)
        {
            //Print
            if (currentLine.Contains("print"))
            {
                string variable = currentLine.Remove(0, "print".Length).Trim();
                string value = String.Empty;

                //Szukanie value
                if (data.intVariables.ContainsKey(variable)) value = data.intVariables[variable].ToString();

                if (data.floatVariables.ContainsKey(variable)) value = data.floatVariables[variable].ToString();

                if (data.stringVariables.ContainsKey(variable)) value = data.stringVariables[variable].ToString();

                //Wypisanie wartosci
                log("------> " + variable + ": " + value, ConsoleMsgType.INFO);

                return true;
            }

            return false;
        }

        bool checkLabel(string currentLine, int lineNumber)
        {
            if (currentLine.Contains(':') && !currentLine.Contains('>') && !currentLine.Contains('<') && !currentLine.Contains("==") && !currentLine.Contains('?'))
            {
                data.addLabel(currentLine, lineNumber);
                return true;
            }

            return false;
        }

        bool makeJump(string currentLine, ref int position)
        {
            //JUMP
            if (currentLine.Contains("jumpnij"))
            {
                string variable = currentLine.Remove(0, "jumpnij".Length).Trim();
                int newLine = position;

                //Szukanie linii do jakiej skoczyc
                if (data.labelsList.ContainsKey(variable))
                {
                    newLine = data.labelsList[variable];
                    position = newLine;
                }

                return true;
            }

            return false;
        }

        bool processIf(string currentLine, ref int position)
        {
            string val1 = String.Empty;
            string val2 = String.Empty;
            string etiq1 = String.Empty;
            string etiq2 = String.Empty;

            if((currentLine.Contains('<') || currentLine.Contains('>') || currentLine.Contains("==")) && currentLine.Contains('?') && currentLine.Contains(':'))
            {
                //log("PROCESSING IF ELSE STATEMENT", ConsoleMsgType.INFO);
                
                // Przetwarzanie zmiennych do relacji
                if (currentLine.Contains('>'))
                {
                    val1 = currentLine.Substring(0, currentLine.IndexOf('>')).Trim();
                    val2 = currentLine.Substring(currentLine.IndexOf('>') + 1, currentLine.IndexOf('?') - currentLine.IndexOf('>')-1).Trim();
                }
                else if (currentLine.Contains('<'))
                {
                    val1 = currentLine.Substring(0, currentLine.IndexOf('<')).Trim();
                    val2 = currentLine.Substring(currentLine.IndexOf('<') + 1, currentLine.IndexOf('?') - currentLine.IndexOf('<') - 1).Trim();
                }
                else if (currentLine.Contains("=="))
                {
                    val1 = currentLine.Substring(0, currentLine.IndexOf("==")).Trim();
                    val2 = currentLine.Substring(currentLine.IndexOf("==") + 2, currentLine.IndexOf('?') - currentLine.IndexOf("==") - 2).Trim();
                }

                // Przetwarzanie skokow do etykiet
                etiq2 = currentLine.Substring(currentLine.LastIndexOf(':') + 1).Trim();
                etiq1 = currentLine.Substring(currentLine.LastIndexOf('?') + 1, currentLine.LastIndexOf(':') - currentLine.LastIndexOf('?') - 1).Trim();

                //log("VAL1: " + val1.Replace(' ', '^'), ConsoleMsgType.INFO);
                //log("VAL2: " + val2.Replace(' ', '^'), ConsoleMsgType.INFO);
                //log("ETIQ1: " + etiq1.Replace(' ', '^'), ConsoleMsgType.INFO);
                //log("ETIQ2: " + etiq2.Replace(' ', '^'), ConsoleMsgType.INFO);

                //Sprawdzenie zmiennych
                //Porownac mozna tylko float i int dlatego zamieniam inta na floata zeby mozna bylo zrobic cross porownanie
                float float1 = 0, float2 = 0;
                try
                {
                    float1 = (float)Double.Parse(val1);
                }
                catch(Exception e)
                {
                    //log("First value is variable" + " --> " + val1.Replace(' ', '^'), ConsoleMsgType.WARNING);

                    if(data.intVariables.ContainsKey(val1))
                    {
                        float1 = (float)data.intVariables[val1];
                    }
                    else if (data.floatVariables.ContainsKey(val1))
                    {
                        float1 = data.floatVariables[val1];
                    }
                }

                try
                { 
                    float2 = (float)Double.Parse(val2);
                }
                catch (Exception e)
                {
                    //log("Second value is variable" + " --> " + val2.Replace(' ', '^'), ConsoleMsgType.WARNING);

                    if (data.intVariables.ContainsKey(val2))
                    {
                        float2 = (float)data.intVariables[val2];
                    }
                    else if (data.floatVariables.ContainsKey(val2))
                    {
                        float2 = data.floatVariables[val2];
                    }
                }

                //log("FLOAT1: " + float1.ToString(), ConsoleMsgType.INFO);
                //log("FLOAT2: " + float2.ToString(), ConsoleMsgType.INFO);

                // Pobranie pozycji etykiet
                int etiq1Pos = position, etiq2Pos = position;
                if(data.labelsList.ContainsKey(etiq1))
                {
                    etiq1Pos = data.labelsList[etiq1];
                }
                if(data.labelsList.ContainsKey(etiq2))
                {
                    etiq2Pos = data.labelsList[etiq2];
                }

                // Wykonanie kodu
                if (currentLine.Contains('>'))
                {
                    if (float1 > float2) position = etiq1Pos;
                    else position = etiq2Pos;
                }
                else if (currentLine.Contains('<'))
                {
                    if (float1 < float2) position = etiq1Pos;
                    else position = etiq2Pos;
                }
                else if (currentLine.Contains("=="))
                {
                    if (float1 == float2) position = etiq1Pos;
                    else position = etiq2Pos;
                }

                return true;
            }

            return false;
        }
    }
}
